﻿using InfoPathHelpers.TemplateExplorer.Definitions;
using InfoPathHelpers.TemplateExplorer.Definitions.RuleDefinitions;
using InfoPathHelpers.UserRoles;
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.XPath;

namespace InfoPathHelpers.TemplateExplorer
{
    public class ManifestExplorer : XmlDocument
    {
        private const string Yes = "yes";
        private XPathNavigator _Navigator;
        private IXmlNamespaceResolver _NamespaceManager;

        public ManifestExplorer(FormContext formContext)
        {
            using(var ManifestStream = formContext.Template.OpenFileFromPackage("manifest.xsf"))
            {
                this.Load(ManifestStream);
                _Navigator = this.CreateNavigator();
                _NamespaceManager = NamespaceResolution.GetNamespaceManager(this);
            }
        }

        public ManifestExplorer(string manifestXml)
        {
            this.LoadXml(manifestXml);
            _Navigator = this.CreateNavigator();
            _NamespaceManager = NamespaceResolution.GetNamespaceManager(this);
        }

        public List<EmailAdapterDefinition> GetEmailAdapters()
        {
            var EmailAdapterNodes = _Navigator.Select("//xsf:emailAdapter", _NamespaceManager);

            var EmailAdapters = new List<EmailAdapterDefinition>();

            foreach (XPathNavigator EmailAdapter in EmailAdapterNodes)
            {
                EmailAdapters.Add(ParseEmailAdapterDefinition(EmailAdapter));
            }
            return EmailAdapters;
        }

        public EmailAdapterDefinition GetEmailAdapter(string name)
        {
            var EmailAdapter = _Navigator.SelectSingleNode("//xsf:emailAdapter[@name = '" + name + "']", _NamespaceManager);
            if(EmailAdapter == null)
            {
                return null;
            }

            return ParseEmailAdapterDefinition(EmailAdapter);
        }

        private EmailAdapterDefinition ParseEmailAdapterDefinition(XPathNavigator node)
        {
            var EmailAdapterDefintition = new EmailAdapterDefinition();
            EmailAdapterDefintition.Name = node.GetAttribute("name", string.Empty);
            EmailAdapterDefintition.SubmitAllowed = node.GetAttribute("submitAllowed", string.Empty) == Yes;

            var To = node.SelectSingleNode("xsf:to", _NamespaceManager);
            if (To != null)
            {
                EmailAdapterDefintition.To = ParseValueDefinition(To);
            }

            var CC = node.SelectSingleNode("xsf:cc", _NamespaceManager);
            if (CC != null)
            {
                EmailAdapterDefintition.CC = ParseValueDefinition(CC);
            }

            var BCC = node.SelectSingleNode("xsf:bcc", _NamespaceManager);
            if (BCC != null)
            {
                EmailAdapterDefintition.BCC = ParseValueDefinition(BCC);
            }

            var Subject = node.SelectSingleNode("xsf:subject", _NamespaceManager);
            if (Subject != null)
            {
                EmailAdapterDefintition.Subject = ParseValueDefinition(Subject);
            }

            var Intro = node.SelectSingleNode("xsf:intro", _NamespaceManager);
            if (Intro != null)
            {
                EmailAdapterDefintition.Intro = Intro.GetAttribute("value", string.Empty);
            }

            var AttachmentFileName = node.SelectSingleNode("xsf:attachmentFileName", _NamespaceManager);
            if (AttachmentFileName != null)
            {
                EmailAdapterDefintition.AttachmentFileName = ParseValueDefinition(AttachmentFileName);
            }

            return EmailAdapterDefintition;
        }

        public List<XmlFileAdapterDefinition> GetXmlFileAdapters()
        {
            var DataObjectNodes = _Navigator.Select("//xsf:dataObject", _NamespaceManager);

            var FileAdapters = new List<XmlFileAdapterDefinition>();

            foreach (XPathNavigator DataObject in DataObjectNodes)
            {
                var XmlFileAdapter = DataObject.SelectSingleNode(".//xsf:xmlFileAdapter", _NamespaceManager);
                if(XmlFileAdapter == null)
                {
                    // This data object is not a file adapter so skip it.
                    continue;
                }
                var FileAdapter = new XmlFileAdapterDefinition();
                FileAdapter.Name = DataObject.GetAttribute("name", string.Empty);
                FileAdapter.SchemaName = DataObject.GetAttribute("schema", string.Empty);
                FileAdapter.IsInitialzedOnLoad = DataObject.GetAttribute("initOnLoad", string.Empty) == Yes;
                FileAdapter.Path = XmlFileAdapter.GetAttribute("fileUrl", string.Empty);

                FileAdapters.Add(FileAdapter);
            }
            return FileAdapters;
        }

        public List<AdoAdapterDefinition> GetAdoAdapters()
        {
            var DataObjectNodes = _Navigator.Select("//xsf:dataObject", _NamespaceManager);

            var AdoAdapters = new List<AdoAdapterDefinition>();

            foreach (XPathNavigator DataObject in DataObjectNodes)
            {
                var AdoAdapter = DataObject.SelectSingleNode(".//xsf:adoAdapter", _NamespaceManager);
                if (AdoAdapter == null)
                {
                    // This data object is not an ADO adapter so skip it.
                    continue;
                }
                var AdoAdapterDefintition = new AdoAdapterDefinition();
                AdoAdapterDefintition.Name = DataObject.GetAttribute("name", string.Empty);
                AdoAdapterDefintition.SchemaName = DataObject.GetAttribute("schema", string.Empty);
                AdoAdapterDefintition.IsInitialzedOnLoad = DataObject.GetAttribute("initOnLoad", string.Empty) == Yes;
                AdoAdapterDefintition.ConnectionString = AdoAdapter.GetAttribute("connectionString", string.Empty);
                AdoAdapterDefintition.CommandText = AdoAdapter.GetAttribute("commandText", string.Empty);
                AdoAdapterDefintition.QueryAllowed = AdoAdapter.GetAttribute("queryAllowed", string.Empty) == Yes;

                AdoAdapters.Add(AdoAdapterDefintition);
            }
            return AdoAdapters;
        }

        public SubmitDefinition GetSubmitDefinition()
        {
            var SubmitDefinition = new SubmitDefinition();

            var SubmitNode = _Navigator.SelectSingleNode("//xsf:submit", _NamespaceManager);

            if (SubmitNode == null)
            {
                SubmitDefinition.SubmitAllowed = false;
                return SubmitDefinition;
            }
            else
            {
                SubmitDefinition.SubmitAllowed = true;
            }


            SubmitDefinition.Caption = SubmitNode.GetAttribute("caption", string.Empty);
            SubmitDefinition.IsMenuItemDisabled = SubmitNode.GetAttribute("disableMenuItem", string.Empty) == Yes;
            SubmitDefinition.AfterSubmitAction = (AfterSubmitActions)Enum.Parse(typeof(AfterSubmitActions), 
                SubmitNode.GetAttribute("onAfterSubmit", string.Empty), true);
            SubmitDefinition.ShowStatusDialog = SubmitNode.GetAttribute("showStatusDialog", string.Empty) == Yes;

            var ErrorMessage = SubmitNode.SelectSingleNode("xsf:errorMessage", _NamespaceManager);
            if(ErrorMessage != null)
            {
                SubmitDefinition.ErrorMessage = ErrorMessage.Value;
            }

            var SuccessMessage = SubmitNode.SelectSingleNode("xsf:successMessage", _NamespaceManager);
            if (SuccessMessage != null)
            {
                SubmitDefinition.SuccessMessage = SuccessMessage.Value;
            }

            var EmailAdapter = SubmitNode.SelectSingleNode("xsf:emailAdapter", _NamespaceManager);
            if(EmailAdapter != null)
            {
                SubmitDefinition.EmailAdapter = ParseEmailAdapterDefinition(EmailAdapter);
            }

            var RuleSetActionNode = SubmitNode.SelectSingleNode("xsf:ruleSetAction", _NamespaceManager);
            if(RuleSetActionNode != null)
            {
                var RuleSetName = RuleSetActionNode.GetAttribute("ruleSet", string.Empty);
                SubmitDefinition.RuleSetAction = GetRuleSet(RuleSetName);
            }

            SubmitDefinition.CustomCode = SubmitNode.SelectSingleNode("xsf:useScriptHandler", _NamespaceManager) != null;

            return SubmitDefinition;
        }

        private ValueDefinition ParseValueDefinition(XPathNavigator node)
        {
            if(node == null)
            {
                throw new ArgumentNullException("node");
            }
            
            return new ValueDefinition()
            {
                Value = node.GetAttribute("value", string.Empty),
                ValueType = (ValueTypes)Enum.Parse(typeof(ValueTypes), node.GetAttribute("valueType", string.Empty), true)
            };
        }

        public List<RuleSetDefinition> GetRuleSets()
        {
            var RuleSetNodes = _Navigator.Select("//xsf:ruleSet", _NamespaceManager);

            var RuleSets = new List<RuleSetDefinition>();

            foreach (XPathNavigator RuleSet in RuleSetNodes)
            {
                RuleSets.Add(ParseRuleSetDefinition(RuleSet));
            }
            return RuleSets;
        }

        public RuleSetDefinition GetRuleSet(string name)
        {
            var RuleSetNode = _Navigator.SelectSingleNode("//xsf:ruleSet[@name = '" + name + "']", _NamespaceManager);

            if(RuleSetNode == null)
            {
                return null;
            }

            return ParseRuleSetDefinition(RuleSetNode);
        }

        private RuleSetDefinition ParseRuleSetDefinition(XPathNavigator node)
        {
            var RuleSetDefinition = new RuleSetDefinition();
            RuleSetDefinition.Name = node.GetAttribute("name", string.Empty);
            var RuleNodes = node.Select("xsf:rule", _NamespaceManager);
            foreach (XPathNavigator RuleNode in RuleNodes)
            {
                RuleSetDefinition.Rules.Add(ParseRuleDefinition(RuleNode));
            }

            return RuleSetDefinition;
        }

        private RuleDefinition ParseRuleDefinition(XPathNavigator node)
        {
            var RuleDefinition = new RuleDefinition();
            RuleDefinition.Caption = node.GetAttribute("caption", string.Empty);
            RuleDefinition.Condition = node.GetAttribute("condition", string.Empty);
            RuleDefinition.IsEnabled = node.GetAttribute("isEnabled", string.Empty) == "yes";
            RuleDefinition.AreRemainingRulesRun = node.SelectSingleNode("xsf:exitRuleSet", _NamespaceManager) != null;

            // Move to the first action.
            node.MoveToFirstChild();
            do
            {
                switch(node.LocalName)
                {
                    case "submitAction":
                        RuleDefinition.Actions.Add(new SubmitActionDefinition() 
                        { 
                            AdapterName = node.GetAttribute("adapter", string.Empty)
                        });
                        break;
                    case "switchViewAction":
                        RuleDefinition.Actions.Add(new SwitchViewActionDefinition()
                        {
                            ViewName = node.GetAttribute("view", string.Empty)
                        });
                        break;
                    case "queryAction":
                        RuleDefinition.Actions.Add(new QueryActionDefinition()
                        {
                            AdapterName = node.GetAttribute("adapter", string.Empty)
                        });
                        break;
                    case "assignmentAction":
                        RuleDefinition.Actions.Add(new AssignmentActionDefinition()
                        {
                            TargetField = node.GetAttribute("targetField", string.Empty),
                            Expression = node.GetAttribute("expression", string.Empty)
                        });
                        break;
                    case "dialogBoxMessageAction":
                        RuleDefinition.Actions.Add(new DialogBoxMessageActionDefinition()
                        {
                            Message = node.Value
                        });
                        break;
                    case "dialogBoxExpressionAction":
                        RuleDefinition.Actions.Add(new DialogBoxExpressionActionDefinition()
                        {
                            Expression = node.Value
                        });
                        break;
                    case "openNewDocumentAction":
                        RuleDefinition.Actions.Add(new OpenNewDocumentActionDefinition()
                        {
                            solutionURI = node.GetAttribute("solutionURI", string.Empty)
                        });
                        break;
                    default:
                        break;
                }
            }
            while (node.MoveToNext(XPathNodeType.Element));

            return RuleDefinition;
        }

        public List<ViewDefinition> GetViews()
        {
            var ViewNodes = _Navigator.Select("//xsf:view", _NamespaceManager);

            var ViewDefinitions = new List<ViewDefinition>();

            foreach(XPathNavigator ViewNode in ViewNodes)
            {
                var MainPane = ViewNode.SelectSingleNode("xsf:mainpane", _NamespaceManager);
                ViewDefinitions.Add(new ViewDefinition()
                    {
                        Name = ViewNode.GetAttribute("name", string.Empty),
                        Caption = ViewNode.GetAttribute("caption", string.Empty),
                        ShowOnMenu = ViewNode.GetAttribute("showMenuItem", string.Empty).Equals("yes"),
                        PrintView = ViewNode.GetAttribute("printView", string.Empty),
                        MainPaneTransform = MainPane.GetAttribute("transform", string.Empty)
                    });
            }

            return ViewDefinitions;
        }

        public List<PackageFileDefinition> GetPackageFiles()
        {
            var PackageFiles = new List<PackageFileDefinition>();
            var FileNodes = _Navigator.Select("//xsf:file", _NamespaceManager);
            foreach(XPathNavigator FileNode in FileNodes)
            {
                PackageFiles.Add(ParsePackageFileDefinition(FileNode));
            }

            return PackageFiles;
        }

        private PackageFileDefinition ParsePackageFileDefinition(XPathNavigator node)
        {
            var PackageFile = new PackageFileDefinition();
            PackageFile.Name = node.GetAttribute("name", string.Empty);
            var PackageFileProperties = node.Select(".//xsf:property", _NamespaceManager);
            foreach(XPathNavigator Property in PackageFileProperties)
            {
                PackageFile.Properties.Add(new PackageFilePropertyDefinition()
                {
                    Name = Property.GetAttribute("name", string.Empty),
                    Type = Property.GetAttribute("type", string.Empty),
                    Value = Property.GetAttribute("value", string.Empty)
                });
            }

            return PackageFile;
        }

        public UserRoleCollection GetUserRoles()
        {
            var UserRoles = new List<UserRole>();

            var RolesNode = _Navigator.SelectSingleNode("//xsf:roles", _NamespaceManager);

            if (RolesNode != null)
            {
                var Default = RolesNode.GetAttribute("default", string.Empty);
                var Initiator = RolesNode.GetAttribute("initiator", string.Empty);
                var RoleNodes = RolesNode.Select("xsf:role", _NamespaceManager);

                foreach (XPathNavigator RoleNode in RoleNodes)
                {
                    var Name = RoleNode.GetAttribute("name", string.Empty);

                    var MembershipNodes = RolesNode.Select("xsf:membership/*[@memberOf = '" + Name + "']",
                        _NamespaceManager);

                    var UserAssignments = new List<UserAssignment>();

                    foreach (XPathNavigator MembershipNode in MembershipNodes)
                    {
                        switch (MembershipNode.LocalName)
                        {
                            case "getUserNameFromData":
                                UserAssignments.Add(new UserAssignment(
                                    MembershipNode.GetAttribute("select", string.Empty),
                                    UserAssignmentType.FormData));
                                break;
                            case "userName":
                                UserAssignments.Add(new UserAssignment(
                                    MembershipNode.GetAttribute("name", string.Empty),
                                    UserAssignmentType.ActiveDirectoryUser));
                                break;
                            case "group":
                                UserAssignments.Add(new UserAssignment(
                                    MembershipNode.GetAttribute("name", string.Empty),
                                    UserAssignmentType.ActiveDirectoryGroup));
                                break;
                            default:
                                break;
                        }
                    }

                    var UserRole = new UserRole(Name, Name == Default, Name == Initiator, UserAssignments);

                    UserRoles.Add(UserRole);
                }
            }

            return new UserRoleCollection(UserRoles);
        }
    }
}

